Promise
Promise是异步的一种解决方案,比传统的回调函数更加合理并且强大。
ES6中,Promise是一个对象,这个对象中,有三个状态:1进行中(pending)、2已成功(fulfilled)、3已失败(rejected)。
Promise可以算是一次性容器,Promise这个对象一旦改变状态,该容器即生命周期结束。
也就是说Promise只能发生一次状态改变,只有两种可能:
1、从进行中转为已成功
2、从进行中转为已失败
正是因为这样的特性,导致这Promise有一些局限性,让Promise只能有短暂的生命周期,如果不改变状态,Promise会一直存在,消耗缓存。
1 2 3 4 5 6 7 8 9
| var promise = new Promise(function(resolve, reject) { if (){ resolve(value); } else { reject(error); } });
|
上面这块代码是Promise的基本用法,resolve和reject是ES6内部的两个函数,我们直接调用就好。
resolve就是从进行中转为成功的状态
reject就是从进行中转为失败的状态
注意!:无论执行resolve还是reject,Promise都会直接结束生命
1 2 3 4 5
| promise.then(function(value) { }, function(error) { });
|
在开发中我们习惯使用then方法来代替resolve和reject麻烦的用法,then方法中接受两个回调,分别代表成功回调和失败回调
1 2 3 4 5 6 7 8 9
| function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
|
上面这块代码是一个Promise实例,实现了触发定时器的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!');
|
上面这块代码证明了Promise对象被创建后就会执行,这也是我所说的,如果要用Promise,请适量而止,并且尽量开门进山的使用异步方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }
|
这是一个加载图片的实例,当图面加载成功的时候把图片吐出,失败时抛出错误。
1 2 3 4 5 6 7 8
| new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); });
|
resolve和reject的位置并不会影响js语句的执行。
Promise版Ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
|
这是一个比较经典的Promise实现的ajax,当返回值为200时抛出成功,并把response吐出。非200时抛出失败。
Promise的方法
1、then()
上文已经提到的then,用于指定成功时的回调函数,接受两个参数(成功、失败)其主要优点在于内部方法中把自己return回来了,所以可以用于链式操作
1 2 3 4 5 6 7
| getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("resolved: ", comments); }, function funcB(err){ console.log("rejected: ", err); });
|
2、catch()
Promise.catch(error)用于指定发生错误时的回调函数,error参数时Promise返回的值
1 2 3 4 5 6
| getJSON('/posts.json').then(function(posts) { }).catch(function(error) { console.log('发生错误!', error); });
|
3、all()
Promise.all()用于多个Promise实例全部执行,我们知道Promise因为用完即死的特性,所以如果用到遍历或者多个异步操作时,不可避免创建多个Promise,而all()方法解决了遍历时的痛苦
1 2 3 4 5 6 7 8 9 10
| // 生成一个Promise对象的数组 var promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
|
Promise.all([1,2,3])接受一个数组,如果所有Promise都失败时或者有一个成功时才会调用all后的回调函数then。
4、resolve()
有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
5、reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
1 2 3 4 5 6 7 8
| var p = Promise.reject('出错了'); var p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) });
|
6、done()
Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
1 2 3 4 5
| asyncFunc() .then(f1) .catch(r1) .then(f2) .done();
|
7、finally()
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
1 2 3 4 5
| server.listen(0) .then(function () { }) .finally(server.stop);
|